/*
 * @(#)JideSplitPaneLayout.java 7/7/2009
 *
 * Copyright 2002 - 2009 JIDE Software Inc. All rights reserved.
 *
 */

package com.element.ui.pane;

import com.element.ui.layout.JideBoxLayout;

import javax.swing.*;
import java.awt.*;
import java.util.Arrays;

/**
 * Layout manager used by JideSplitPane.
 */
public class JideSplitPaneLayout extends JideBoxLayout {
	public JideSplitPaneLayout(Container target) {
		super(target);
		setResetWhenInvalidate(false);
	}

	public JideSplitPaneLayout(Container target, int axis) {
		super(target, axis);
		setResetWhenInvalidate(false);
	}

	public JideSplitPaneLayout(Container target, int axis, int gap) {
		super(target, axis, gap);
		setResetWhenInvalidate(false);
	}

	int getDividerLocation(int index) {
		if (_componentSizes == null) {
			return -1;
		}
		if (index < 0 || (index + 1) << 1 >= _componentSizes.length)
			return -1;

		boolean ltr = _target.getComponentOrientation().isLeftToRight();
		boolean reversed = !ltr && ((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT;

		int location = 0;
		if (reversed) {
			for (int i = _componentSizes.length - 1; i >= (index * 2) + 1; i--)
				location += _componentSizes[i];
		} else {
			for (int i = 0; i < (index * 2) + 1; i++)
				location += _componentSizes[i];
		}

		Insets insets = _target.getInsets();
		if (insets != null) {
			if (((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT) {
				location += reversed ? insets.right : insets.left;
			} else {
				location += insets.top;
			}
		}
		return location;
	}

	@SuppressWarnings({"RedundantCast"})
	int setDividerLocation(int index, int location, boolean isOriginator) {
		int oldLocation = getDividerLocation(index);
		if (oldLocation == -1 || oldLocation == location)
			return -1;
		boolean ltr = _target.getComponentOrientation().isLeftToRight();
		boolean reversed = !ltr && ((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT;
		int prevIndex = reversed ? 2 * index + 2 : 2 * index;
		int nextIndex = reversed ? 2 * index : 2 * index + 2;
		int nextDividerIndex = reversed ? index - 1 : index + 1;
		int prevDividerIndex = reversed ? index + 1 : index - 1;
		int flexibleNextIndex;
		int flexiblePrevIndex;

		if (reversed) {
			while (nextIndex >= 0 && !isPaneVisible(nextIndex)) {
				nextIndex -= 2;
				nextDividerIndex--;
			}

			while (prevIndex < _componentSizes.length && !isPaneVisible(prevIndex)) {
				prevIndex += 2;
				prevDividerIndex++;
			}

			flexibleNextIndex = nextIndex;
			while (flexibleNextIndex >= 0 &&
					(getConstraintMap().get(_target.getComponent(flexibleNextIndex)) == JideBoxLayout.FIX || !isPaneVisible(flexibleNextIndex))) {
				flexibleNextIndex -= 2;
			}
			if (flexibleNextIndex < 0) {
				return -1;
			}

			flexiblePrevIndex = prevIndex;
			while (flexiblePrevIndex < _componentSizes.length &&
					(getConstraintMap().get(_target.getComponent(flexiblePrevIndex)) == JideBoxLayout.FIX || !isPaneVisible(flexiblePrevIndex))) {
				flexiblePrevIndex += 2;
			}
			if (flexiblePrevIndex >= _componentSizes.length) {
				return -1;
			}
		} else {
			while (nextIndex < _componentSizes.length && !isPaneVisible(nextIndex)) {
				nextIndex += 2;
				nextDividerIndex++;
			}

			while (prevIndex >= 0 && !isPaneVisible(prevIndex)) {
				prevIndex -= 2;
				prevDividerIndex--;
			}

			flexibleNextIndex = nextIndex;
			while (flexibleNextIndex < _componentSizes.length &&
					(getConstraintMap().get(_target.getComponent(flexibleNextIndex)) == JideBoxLayout.FIX || !isPaneVisible(flexibleNextIndex))) {
				flexibleNextIndex += 2;
			}
			if (flexibleNextIndex >= _componentSizes.length) {
				return -1;
			}

			flexiblePrevIndex = prevIndex;
			while (flexiblePrevIndex >= 0 &&
					(getConstraintMap().get(_target.getComponent(flexiblePrevIndex)) == JideBoxLayout.FIX || !isPaneVisible(flexiblePrevIndex))) {
				flexiblePrevIndex -= 2;
			}
			if (flexiblePrevIndex < 0) {
				return -1;
			}
		}

		if (isOriginator
				&& getConstraintMap().get(_target.getComponent(nextIndex)) == JideBoxLayout.FIX
				&& getConstraintMap().get(_target.getComponent(prevIndex)) == JideBoxLayout.FIX) {
			return -1;
		}

		if (location > oldLocation) {
			int size = _componentSizes[2 * index + 1];
			if (getConstraintMap().get(_target.getComponent(nextIndex)) == JideBoxLayout.FIX) {
				size += _componentSizes[nextIndex];
			} else {
				if (((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT) {
					size += _target.getComponent(nextIndex).getMinimumSize().getWidth();
				} else {
					size += _target.getComponent(nextIndex).getMinimumSize().getHeight();
				}
			}

			int nextDividerLocation = getDividerLocation(nextDividerIndex);
			if (nextDividerLocation < 0) {
				if (((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT) {
					location = Math.min(location, _target.getWidth() - size);
				} else {
					location = Math.min(location, _target.getHeight() - size);
				}
			} else if (location + size > nextDividerLocation) {
				int actualLocation = setDividerLocation(nextDividerIndex, location + size, false);
				if (actualLocation == -1) {
					return -1;
				}
				location = actualLocation - size;
			}
			if (getConstraintMap().get(_target.getComponent(nextIndex)) != JideBoxLayout.FIX) {
				_componentSizes[nextIndex] -= location - oldLocation;
			}
			if (isOriginator) {
				_componentSizes[flexiblePrevIndex] += location - oldLocation;
			} else if (getConstraintMap().get(_target.getComponent(prevIndex)) != JideBoxLayout.FIX) {
				_componentSizes[prevIndex] += location - oldLocation;
			}
		} else if (location < oldLocation) {
			int size = 0;
			if (prevDividerIndex >= 0) {
				size = _componentSizes[prevIndex - 1];
			}
			if (getConstraintMap().get(_target.getComponent(prevIndex)) == JideBoxLayout.FIX) {
				size += _componentSizes[prevIndex];
			} else {
				if (((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT) {
					size += _target.getComponent(prevIndex).getMinimumSize().getWidth();
				} else {
					size += _target.getComponent(prevIndex).getMinimumSize().getHeight();
				}
			}

			int prevDividerLocation = getDividerLocation(prevDividerIndex);
			if (prevDividerLocation < 0) {
				location = Math.max(location, size);
			} else if (location - size < prevDividerLocation) {
				int actualLocation = setDividerLocation(prevDividerIndex, location - size, false);
				if (actualLocation == -1) {
					return -1;
				}
				location = actualLocation + size;
			}
			if (getConstraintMap().get(_target.getComponent(prevIndex)) != JideBoxLayout.FIX) {
				_componentSizes[prevIndex] -= oldLocation - location;
			}
			if (isOriginator) {
				_componentSizes[flexibleNextIndex] += oldLocation - location;
			} else if (getConstraintMap().get(_target.getComponent(nextIndex)) != JideBoxLayout.FIX) {
				_componentSizes[nextIndex] += oldLocation - location;
			}
		}

		if (_target instanceof JideSplitPane) {
			((JideSplitPane) _target).firePropertyChange(JideSplitPane.PROPERTY_DIVIDER_LOCATION, oldLocation, location);
		} else {
			_target.firePropertyChange(JideSplitPane.PROPERTY_DIVIDER_LOCATION, oldLocation, location);
		}
		((JideSplitPane) _target).revalidate();

		if (((JideSplitPane) _target).isProportionalLayout()) {
			replaceProportions();
			return location;
		}

		if (((JideSplitPane) _target).getOrientation() == JideSplitPane.HORIZONTAL_SPLIT) {
			for (int changedIndex = 0; changedIndex < _target.getComponentCount(); changedIndex++) {
				Component component = _target.getComponent(changedIndex);
				if (component instanceof JComponent && !(component instanceof JideSplitPaneDivider)) {
					((JComponent) component).setPreferredSize(new Dimension(_componentSizes[changedIndex], component.getPreferredSize().height));
				}
			}
		} else {
			for (int changedIndex = 0; changedIndex < _target.getComponentCount(); changedIndex++) {
				Component component = _target.getComponent(changedIndex);
				if (component instanceof JComponent && !(component instanceof JideSplitPaneDivider)) {
					((JComponent) component).setPreferredSize(new Dimension(component.getPreferredSize().width, _componentSizes[changedIndex]));
				}
			}
		}
		return location;
	}

	private boolean isPaneVisible(int index) {
		// Considering collapse/expand feature, we need consider the visibility of divider as well
		return index < _componentSizes.length && (_componentSizes[index] != 0 || index - 1 < 0 || _componentSizes[index - 1] != 0);
	}

	/**
	 * Uses the component sizes to generate a new array of proportions, and replaces the existing one.
	 */
	private void replaceProportions() {
		((JideSplitPane) _target).setProportions(deduceProportions());
	}

	/**
	 * Uses the component sizes to generate a new array of proportions.
	 *
	 * @return a new array of proportions
	 */
	private double[] deduceProportions() {
		double total = 0.0; // Total height or width of contained panes (not including dividers)
		for (int i = 0; i < _componentSizes.length; i += 2) // Note we're skipping dividers
			total += _componentSizes[i];
		double[] newProportions;
		if (total == 0.0)
			newProportions = null;
		else {
			newProportions = new double[(_componentSizes.length - 1) / 2];
			for (int i = 0; i < newProportions.length; ++i)
				newProportions[i] = _componentSizes[i * 2] / total;
		}
		return newProportions;
	}

	// For proportional layouts, override the key box layout method:
	@Override
	protected boolean calculateComponentSizes(int availableSize, int startIndex, int endIndex) {
		// Just go to super for non-proportional layout, or if there are no more
		// than one component.
		if (!((JideSplitPane) _target).isProportionalLayout()
				|| _target.getComponentCount() <= 1)
			return super.calculateComponentSizes(availableSize, startIndex, endIndex);

		// If we have no set proportions, either call super to get initial
		// proportions or set them up as even.
		if (((JideSplitPane) _target).getProportions() == null) {
			if (!((JideSplitPane) _target).isInitiallyEven()) {
				return super.calculateComponentSizes(availableSize, startIndex, endIndex);
//<syd_0033> Luke's change
//   David: We now wait to set proportions until the user sets them directly.  Otherwise recreated
//   docked frames end up with tiny sizes instead of using preferred size, since they are
//   initially added to their ContainerContainers without their contents.
//                    _proportions = deduceProportions(); // Note no call to setProportions: no event
//</syd_0033>
			}
			// What remains is the "initially even" logic:
			int c = ((JideSplitPane) _target).getPaneCount();
			double[] p = new double[c - 1];
			Arrays.fill(p, 1.0 / c);
			((JideSplitPane) _target).internalSetProportions(p); // Note no call to setProportions: no event may be sent here
		}

		// Spin through the dividers, setting up their sizes and subtracting from available.
		for (int i = 1; i < _target.getComponentCount(); i += 2) {
			if (_target.getComponent(i).isVisible())
				_componentSizes[i] = ((JideSplitPane) _target).getDividerSize();
			availableSize -= _componentSizes[i];
		}

		if (availableSize < 0)
			return false;

		// Then spin through the panes and set their sizes according to the
		// proportions.
		double[] proportions = ((JideSplitPane) _target).getProportions();
//            double last = 1.0;
//            for (double p : proportions) last -= p;
//            double total = 0.0;
//            for (int i = 0; i < getComponentCount(); i += 2) {
//                int j = i / 2;
//                if (getComponent(i).isVisible())
//                    total += (j < proportions.length) ? proportions[j] : last;
//            }
		int size = availableSize;
		for (int i = 0; i < proportions.length; ++i) {
			int j = i * 2;
			if (_target.getComponent(j).isVisible()) {
				double d = proportions[i]/* / total*/;
				if (d <= 1.0)
					_componentSizes[j] = (int) (0.5 + size * d);
			}
			availableSize -= _componentSizes[j];
		}
		// Now set the last one to whatever is left over.
		if (availableSize < 0)
			return false;

		_componentSizes[_componentSizes.length - 1] = availableSize;
		return true;
	}
}
